using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.FSharp.Collections;
namespace Structures
{
    public static class ExtensionMethods
    {
        public static float GetVolume(this BoundingSphere sphere)
        {
            return (4.0f / 3.0f) * MathHelper.Pi * ((float)Math.Pow(sphere.Radius, 3));
        }

        public static float GetVolume(this BoundingBox box)
        {

            var corners = box.GetCorners().ToList();

            var p1 = corners.First();
            corners.RemoveAt(0);

            var p2 = corners.Where(p => Vector3.Distance(p1, p) == corners.Min(mdist => Vector3.Distance(p1, mdist))).First();
            corners.Remove(p2);

            var p3 = corners.Where(p => Vector3.Distance(p1, p) == corners.Min(mdist => Vector3.Distance(p1, mdist))).First();
            corners.Remove(p3);


            var tmpPoint2 = corners.Where(p => Vector3.Distance(p2, p) == corners.Min(mdist => Vector3.Distance(p2, mdist))).First();
            corners.Remove(tmpPoint2);

            var tmpPoint3 = corners.Where(p => Vector3.Distance(p3, p) == corners.Min(mdist => Vector3.Distance(p3, mdist))).First();
            corners.Remove(tmpPoint3);

            var p4 = corners.Where(p => Vector3.Distance(p1, p) == corners.Min(mdist => Vector3.Distance(p1, mdist))).First();


            var s1 = Vector3.Distance(p1, p2);
            var s2 = Vector3.Distance(p1, p3);
            var s3 = Vector3.Distance(p1, p4);

            return s1 * s2 * s3;
        }

        public static Nullable<float> Intersects(this Ray ray, Bounding bounding)
        {
            return bounding.Intersects(ray);
        }
        public static Nullable<Ray> GetRay(this Viewport viewport, Vector2 touch, Matrix view, Matrix projection)
        {
            if (touch.X >= 0 && touch.X <= viewport.Width && touch.Y >= 0 && touch.Y <= viewport.Height)
            {

                var near_touch = viewport.Unproject(new Vector3(touch.X, touch.Y, 0.0f), projection, view, Matrix.Identity);
                var far_touch = viewport.Unproject(new Vector3(touch.X, touch.Y, 1.0f), projection, view, Matrix.Identity);
                var touch_dir = Vector3.Normalize(far_touch - near_touch);
                return new Microsoft.Xna.Framework.Ray(near_touch, touch_dir);
            }
            else return null;
        }
        public static IEnumerable<X3dModel> ToEnumerable(this FSharpList<X3dModel> fList)
        {
            return SeqModule.OfList<X3dModel>(fList);
        }
        public static FSharpList<X3dModel> ToFsharp(this IEnumerable<X3dModel> iList)
        {
            return ListModule.OfSeq<X3dModel>(iList);
        }
    }
}
